//
// Created by hss on 2017/8/16.
//

#include "SplitFile.h"

//*********************************
JavaVM * g_jvm = NULL;
jobject g_obj= NULL;
//*****************wen文件大小*********
long  get_file_size(const char * path){
    FILE * fp = fopen(path,"rb");//打开一个文件，文件必须存在，只读
    fseek(fp,0,SEEK_END);
    long ret = ftell(fp);
    fclose(fp);
    return  ret;
}




JNIEXPORT void JNICALL native_diff
        (JNIEnv * env, jclass clazz, jstring  path, jstring  pattern_path,  jint file_num){

    const  char  * pathStr = env->GetStringUTFChars(path,NULL);
    const  char  * patternStr =   env->GetStringUTFChars(pattern_path,NULL);
        LOGI("pathStr =  %s",pathStr);
    //申请二位字符数组，存放文件名
    char **patches = (char**)malloc(sizeof(char*)*file_num);


    for (int i=0;i<file_num;i++){
        //每个文件名申请地址
        patches[i] = (char*)malloc(sizeof(char)*100);
        //需要分割的文件vibrato.mp4
        //需要分割的文件vibrato_n.mp4
        sprintf(patches[i],patternStr,i);
        LOGI("patches path : %s", patches[i]);
    }

    long fileSize = get_file_size(pathStr);
    FILE * fpr = fopen(pathStr,"rb");
    /**
     * 判断file size 大小能否被file_num 整除
     * 能整除就评分
     * 不能整除就先分 file_num - 1
     */
    if (fileSize % file_num == 0){
        long part = fileSize / file_num;
        for (int i=0;i<file_num;i++){
            FILE * fpw = fopen(patches[i],"wb");//文件已经存在 就删除  只运行写
            for (long j=0;j<part;j++){
                fputc(fgetc(fpr),fpw);
            }
            fclose(fpw);
        }

    }else {
        int part = fileSize/(file_num-1);
        for (int i=0;i<file_num-1;i++){
            FILE * fpw = fopen(patches[i],"wb");//文件已经存在 就删除  只运行写
            for (long j=0;j<part;j++){
                fputc(fgetc(fpr),fpw);
            }
            fclose(fpw);
        }

        FILE * fpw = fopen(patches[file_num-1],"wb");//文件已经存在 就删除  只运行写
        for (long j=0,size = fileSize % (file_num-1);j<size;j++){
            fputc(fgetc(fpr),fpw);
        }
        fclose(fpw);
    }
    fclose(fpr);


    for (int i=0;i<file_num;i++){
        free(patches[i]);
    }

    free(patches);

    env->ReleaseStringUTFChars(path,pathStr);
    env->ReleaseStringUTFChars(pattern_path,patternStr);
}

JNIEXPORT void JNICALL native_merge
        (JNIEnv * env, jclass clazz, jstring  merge_path, jstring  pattern_path,  jint file_num){

    const  char  * pathStr = env->GetStringUTFChars(merge_path,NULL);
    const  char  * patternStr =   env->GetStringUTFChars(pattern_path,NULL);

    //申请二位字符数组，存放文件名
    char **patches = (char**)malloc(sizeof(char*)*file_num);


    for (int i=0;i<file_num;i++){
        //每个文件名申请地址
        patches[i] = (char*)malloc(sizeof(char)*100);
        //需要分割的文件vibrato.mp4
        //需要分割的文件vibrato_n.mp4
        sprintf(patches[i],patternStr,i);
        LOGI("patches path : %s", patches[i]);
    }

    FILE * fpw = fopen(pathStr,"wb");//

    for (int i=0;i<file_num;i++){
        long fileSize= get_file_size(patches[i]);
        FILE * fpr = fopen(patches[i],"rb");//
        for (long j=0 ;j<fileSize;j++){
            fputc(fgetc(fpr),fpw);
        }
        fclose(fpr);
    }

    fclose(fpw);

    for (int i=0;i<file_num;i++){
        free(patches[i]);
    }

    free(patches);

    env->ReleaseStringUTFChars(merge_path,pathStr);
    env->ReleaseStringUTFChars(pattern_path,patternStr);
}

//********************* jni thread*************************
void * thread_fun(void * arg){

    JNIEnv * env;
    jclass  cls;
    jmethodID  jmethodID1,jmethodID2;
    if (g_jvm->AttachCurrentThread(&env,NULL) != JNI_OK){
        LOGD("%s AttachCurrentThread error",__FUNCTION__);
        return NULL;
    }

    cls = env->GetObjectClass(g_obj);
    if (cls == NULL){
        LOGD("find g_obj class  ==  NULL");
        goto err;
    }

    jmethodID1 = env->GetStaticMethodID(cls,"fromJniStatic","(I)V");

    if (jmethodID1 == NULL){
        goto err;
    }

    LOGD("CallStaticVoidMethod  fromJniStatic");

    env->CallStaticVoidMethod(cls,jmethodID1,(long)arg);


    jmethodID2 = env->GetMethodID(cls,"fromJni","(I)V");
    if (jmethodID2 == NULL){
        goto err;
    }
    LOGD("CallVoidMethod  fromJni");

    env->CallVoidMethod(g_obj,jmethodID2, (long)arg);

    err:
    if (g_jvm->DetachCurrentThread() != JNI_OK){
        LOGD("%s DetachCurrentThread error",__FUNCTION__);
    }
    pthread_exit(0);
}


/*
 * Class:     com_hss_splitfile_SplitFile
 * Method:    newJniThread
 * Signature: ()V
 */
JNIEXPORT void JNICALL native_newJniThread
        (JNIEnv * env, jobject obj){
        LOGD("new thread begin");
    pthread_t pt[5];
    for (int i=0;i<5;i++){
        pthread_create(&pt[i],NULL,&thread_fun,(void*)i);
    }

}

/*
 * Class:     com_hss_splitfile_SplitFile
 * Method:    setJniEnv
 * Signature: ()V
 */
JNIEXPORT void JNICALL native_setJniEnv
        (JNIEnv * env , jobject obj){
    //保存jvm
        env->GetJavaVM(&g_jvm);
    //保持activity对象
    g_obj = env->NewGlobalRef(obj);
}


JNIEXPORT void JNICALL native_releaseJniEnv
        (JNIEnv * env, jobject obj){
    env->DeleteGlobalRef(g_obj);
}

//**************************加载********************************
static const JNINativeMethod gMethods[] = {
        {
                "diff", "(Ljava/lang/String;Ljava/lang/String;I)V",(void*)native_diff
        },
        {
                "merge", "(Ljava/lang/String;Ljava/lang/String;I)V",(void*)native_merge
        },
        {
                "newJniThread", "()V",(void*)native_newJniThread
        },
        {
                "setJniEnv", "()V",(void*)native_setJniEnv
        },
        {
                "releaseJniEnv", "()V",(void*)native_releaseJniEnv
        },


};


static  int registerNatives(JNIEnv * env){
    jclass  jclass1;
    jclass1 = env->FindClass("com/hss/splitfile/SplitFile");

    if (jclass1 == NULL){
        LOGD("FindClass(\"com/hss/splitfile/SplitFile\")  == NULL");
        return JNI_FALSE;
    }
    if (env->RegisterNatives(jclass1,gMethods,NELEM(gMethods))< 0){
        LOGD("RegisterNatives < 0");
        return JNI_FALSE;
    }

    return JNI_TRUE;
}

JNIEXPORT  jint JNI_OnLoad(JavaVM * vm, void * reserved){
    LOGD("jni onload begin");
    JNIEnv * env = NULL;
    jint  result= -1;
    if ((*vm).GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK){
        LOGD("GetEnv error");
        return -1;
    }
    assert("env != null");
    registerNatives(env);
    return  JNI_VERSION_1_4;
}